package com.retelimiter.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.util.concurrent.RateLimiter;
import com.retelimiter.annotation.ExtRateLimiter;

@Aspect
@Component
public class RateLimiterAop {
	/**
	 * 定义存放令牌桶的容器
	 */
	private Map<String,RateLimiter> map=new ConcurrentHashMap<String,RateLimiter>();
	/**
	 * 定义扫包范围
	 */
	@Pointcut("execution(public * com.retelimiter.controller.*.*(..))")
	public void rlAop() {
		
	}
	/**
	 * 环绕通知
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("rlAop()")
	public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		//1.获取拦截访问的方法
		Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
		//2.判断是否包含ExtRateLimiter这个注解
		ExtRateLimiter extRateLimiter=method.getDeclaredAnnotation(ExtRateLimiter.class);
		if(extRateLimiter==null){
			//3.没有注解直接调用方法执行
			return proceedingJoinPoint.proceed();
		}
		//4.有注解获取注解上的属性值调用RateLimiter的方法限流
		double value= extRateLimiter.value(); //获取向令牌桶中存放令牌的速率
		long timeOut=extRateLimiter.timeOut();//获取超时时间
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String uri=request.getRequestURI();//获取请求的uri
		RateLimiter rateLimiter=null;
		if(map.containsKey(uri)){//判断令牌桶中是否存有这个请求的令牌桶
			rateLimiter=map.get(uri);	//有
		}else{
			//没有
			rateLimiter=RateLimiter.create(value);
			map.put(uri, rateLimiter);
		}
		boolean acquire =rateLimiter.tryAcquire(timeOut,TimeUnit.MILLISECONDS);
		if(!acquire){
			//表示没有拿到令牌  //6.请求没有拿到令牌调用本地降级的方法
			fullBack();
			return "你在怎么抢，也抢不到，因为会一直等待的，你先放弃吧！";
		}
		return proceedingJoinPoint.proceed();//5.请求拿到令牌调用方法执行
	}

	/**
	 * 本地的降级方法
	 * 
	 * @throws IOException
	 */
	private void fullBack() throws IOException {
		// 执行服务降级处理
		System.out.println("执行降级方法,亲,服务器忙！请稍后重试!");
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletResponse response = attributes.getResponse();
		response.setHeader("Content-type", "text/html;charset=UTF-8");
		PrintWriter writer = response.getWriter();
		try {
			writer.println("执行降级方法,亲,服务器忙！请稍后重试!");
		} catch (Exception e) {

		} finally {
			writer.flush();
			writer.close();
		}
	}

}
